package com.java.mall_imooc.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.java.mall_imooc.common.enums.PayPlatformEnum;
import com.java.mall_imooc.common.mq.RabbitSender;
import com.java.mall_imooc.entity.PayInfo;
import com.java.mall_imooc.mapper.PayInfoDao;
import com.java.mall_imooc.service.PayService;
import com.lly835.bestpay.enums.BestPayPlatformEnum;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.enums.OrderStatusEnum;
import com.lly835.bestpay.model.PayRequest;
import com.lly835.bestpay.model.PayResponse;
import com.lly835.bestpay.service.BestPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @author jiangli
 * @since 2020-03-13 23:22
 */
@Service
@Slf4j
@AllArgsConstructor
public class PayServiceImpl implements PayService {
    private BestPayService bestPayService;
    private PayInfoDao payInfoDao;
    private AmqpTemplate amqpTemplate;
    private RabbitSender rabbitSender;

    /**
     * 创建/发起支付
     */
    @Override
    @Transactional
    public PayResponse create(String orderNo, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum) {
        // 写入数据库
        PayInfo payInfo = PayInfo.builder().orderNo(orderNo).payPlatform(PayPlatformEnum.getPayPlatform(bestPayTypeEnum))
                .platformStatus(OrderStatusEnum.NOTPAY.name()).payAmount(amount).build();
        payInfoDao.insert(payInfo);
        // 发起支付
        PayRequest payRequest = new PayRequest();
        payRequest.setPayTypeEnum(bestPayTypeEnum);
        payRequest.setOrderId(orderNo);
        payRequest.setOrderAmount(amount.doubleValue());
        payRequest.setOrderName("JL商城订单-20201314");

        return bestPayService.pay(payRequest);
    }

    /**
     * 异步通知处理
     */
    public String asyncNotify(String xmlData) {
        //1.校验签名
        PayResponse payResponse = bestPayService.asyncNotify(xmlData);
        //2.金额校验(从数据库查询订单金额)
        PayInfo payInfo = payInfoDao.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getOrderNo, payResponse.getOrderId()));
        if (payInfo == null) {
            // 严重异常(正常情况下是不会发生的) 发出告警:短信、邮件
            throw new RuntimeException("支付回调orderNo不能存在:" + payResponse.getOrderId());
        }
        // 2.1判断订单状态,如果订单状态不是"已支付"
        if (!OrderStatusEnum.SUCCESS.name().equals(payInfo.getPlatformStatus())) {
            //2.2比较金额.Double比较大小有精度丢失问题.金钱的比较用BigDecimal
            if (payInfo.getPayAmount().compareTo(BigDecimal.valueOf(payResponse.getOrderAmount())) != 0) {
                throw new RuntimeException("支付回调中的金额和数据库中不一致!orderNo=" + payInfo.getOrderNo());
            }
            //3.修改订单支付状态
            payInfo.setPlatformStatus(OrderStatusEnum.SUCCESS.name());
            // 第三方支付流水号
            payInfo.setPlatformNumber(payResponse.getOutTradeNo());
            payInfo.setUpdateTime(new Date());
            payInfoDao.updateById(payInfo);
        }

        // 向order模块发送消息 修改订单状态
        // 这里没有指定交换机，因此默认发送到了配置中的：imooc.exchange
//        amqpTemplate.convertAndSend("order.pay", JSONUtil.toJsonStr(payInfo));
        rabbitSender.sendMessage("imooc.exchange","order.pay", JSONUtil.toJsonStr(payInfo));

        //4.通知微信/支付宝
        if (BestPayPlatformEnum.WX.equals(payResponse.getPayPlatformEnum())) {
            return "<xml>\n" +
                    "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                    "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                    "</xml>";
        } else if (BestPayPlatformEnum.ALIPAY.equals(payResponse.getPayPlatformEnum())) {
            return "success";
        }
        throw new RuntimeException("异步通知中错误的支付平台!");
    }

    @Override
    public PayInfo queryByOrderNo(String orderNo) {
        return payInfoDao.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getOrderNo, orderNo));
    }
}
